package org.kent.w.base.common.auth;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.joda.time.DateTime;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Auth0
 *
 * @author wu.jigang@geely.com
 * @version 1.0
 * @Date :Created by 2020/3/5.
 */
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class JwtHelper {

    /** 密钥 **/
    private String secret;

    /** 签名是由谁生成 **/
    private String issUser;

    /** 签名主题 **/
    private String subject = "This is a token";

    /** 签名的观众 **/
    private String audience = "XXX";


    /**
     * 创建token
     *
     * @param claims
     * @param expireSecond
     * @return
     */
    public String createToken(Map<String, String> claims, long expireSecond) {

        if (MapUtils.isEmpty(claims)) {
            return null;
        }

        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);

            Map<String, Object> map = new HashMap<>();

            DateTime now = DateTime.now();
            DateTime expireDate = now.secondOfDay().addToCopy(expireSecond);

            map.put("alg", "HS256");
            map.put("typ", "JWT");

            JWTCreator.Builder builder = JWT.create()
                    // 设置头部信息 Header
                    .withHeader(map)
                    .withIssuer(issUser)
                    .withSubject(subject)
                    .withAudience(audience)
                    // 签名创建时间
                    .withIssuedAt(now.toDate())
                    // 签名过期时间
                    .withExpiresAt(expireDate.toDate());

            // 设置 载荷 Payload
            claims.forEach(builder::withClaim);

            return builder.sign(algorithm);

        } catch (JWTCreationException e) {

            log.error("Token创建失败, claims=[{}]", claims, e);
            return null;
        }
    }

    /**
     * token校验并返回Claim
     *
     * @param token
     * @return
     */
    public Map<String, String> verifyToken(String token) {

        Map<String, Claim> claimMap;

        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);

            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer(issUser)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            claimMap = jwt.getClaims();

        } catch (JWTVerificationException e) {
            log.error("Token解析失败, token={}", token, e);
            return null;
        }

        if (MapUtils.isEmpty(claimMap)) {
            return null;
        }

        return claimMap.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, claimEntry -> claimEntry.getValue().asString(), (a, b) -> b));
    }
}
